/*
 * ByteQueue.h
 *
 *  Created on: 22/06/2014
 *      Author: Vitor
 */

#ifndef BYTEQUEUE_H_
#define BYTEQUEUE_H_

#include <vector>
#include <string>
#include <string.h>

class ByteQueue {
private:
	std::vector<char> buf;
public:
	ByteQueue(std::vector<char> buffer);
	ByteQueue(size_t size = 0);
	size_t size() const;
	void resize(size_t size);
	void* ptr() const;

	std::vector<char>& get_raw() {
		return buf;
	}

	ByteQueue append(ByteQueue other);
	bool empty() {
		return buf.empty();
	}
	ByteQueue& push(const void* data, size_t maxlen);
	ByteQueue& push(const std::string& data, bool with_null_termination = true);
	template<typename T> ByteQueue& push(T data) {
		buf.insert(buf.end(), (char*) &data, ((char*) &data) + sizeof(T));
		return *this;
	}

	template<typename T> ByteQueue& push_front(T data) {
		buf.insert(buf.begin(), (char*) &data, ((char*) &data) + sizeof(T));
		return *this;
	}

	size_t pop(void* data, size_t maxlen);
	std::string pop(size_t maxlen);
	template<typename T> T pop() {
		T data = 0;
		size_t total = sizeof(T) <= buf.size() ? sizeof(T) : buf.size();
		if (!total)
			return data;
		memcpy((void*) &data, (const void*) &buf[0], total);
		buf.erase(buf.begin(), buf.begin() + total);
		return data;
	}
};

template<> inline ByteQueue& ByteQueue::push<std::string>(std::string data) {
	buf.insert(buf.end(), (char*) data.c_str(),
			((char*) data.c_str()) + data.size());
	buf.push_back(char(0));
	return *this;
}

template<> inline std::string ByteQueue::pop<std::string>() {
	std::string data;
	char c;
	while ((c = pop<char>()) != '\0')
		data += c;
	return data;
}
;

#endif /* BYTEQUEUE_H_ */
